import { PickingInfo } from "@babylonjs/core/Collisions/pickingInfo";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
import { EventState } from "@babylonjs/core/Misc/observable";
import { WebXRMotionControllerTeleportation } from "@babylonjs/core/XR/features/WebXRControllerTeleportation";
import { WebXRDefaultExperience } from "@babylonjs/core/XR/webXRDefaultExperience";
import { WebXRFeatureName } from "@babylonjs/core/XR/webXRFeaturesManager";
import { WebXRInput } from "@babylonjs/core/XR/webXRInput";
import { Scene } from "@babylonjs/core/scene";
import { CreateHandleTo, ICallback, IHandle, RemoveHandle } from '../../data/data-handle';


/**
 * 传送移动参数对象
 * @param xrInput xr输入
 * @param floorMeshes 地面网格集合
 * @param floorSize 默认地面大小（如果没有设置地面网格集合）
 * @param floorTessellation 默认地面分段数
 * @param floorPosition 默认地面位置
 * @param backwardsTeleportationDistance 向后传送时的移动距离
 * @param backwardsMovementEnabled 启用向后移动
 * @param rotationEnabled 启用旋转
 * @param rotationAngle 每次旋转角度
 * @param parabolicRayEnabled 启用投射抛物线
 * @param parabolicCheckRadius 投射抛物线半径
 * @param teleportationEnabled 启用传送
 * @param straightRayEnabled 启用直线射线
 * @param snapPointsOnly 仅投射吸附到点
 * @param snapPositions 要投射吸附的点集
 * @param snapToPositionRadius 吸附半径
 * @param useMainComponentOnly 仅使用控制器主部件
 * @param teleportationTargetMesh 传送目标网格（表示目的的网格）
 * @param renderingGroupID 渲染顺序值（目标网格等的）
 * @param pickBlockerMeshes 障碍物网格
 * @param forceHandedness 设置使用的惯用手
 */
interface IVRMotionTeleportationOptions {
    xrInput?: WebXRInput,
    floorMeshes?: AbstractMesh[],
    floorSize?: number,
    floorTessellation?: number,
    floorPosition?: Vector3,
    backwardsTeleportationDistance?: number,
    backwardsMovementEnabled?: boolean,
    rotationEnabled?: boolean,
    rotationAngle?: number,
    parabolicRayEnabled?: boolean,
    parabolicCheckRadius?: number,
    teleportationEnabled?: boolean,
    straightRayEnabled?: boolean,
    snapPointsOnly?: boolean,
    snapPositions?: Vector3[],
    snapToPositionRadius?: number,
    useMainComponentOnly?: boolean,
    teleportationTargetMesh?: AbstractMesh,
    renderingGroupID?: number,
    pickBlockerMeshes?: AbstractMesh[],
    forceHandedness?: XRHandedness,
}


/**
 * 传送移动
 * @param xr xr实例
 * @param scene 所在场景
 * @param options 参数对象
 */
class VRMotionTeleportation {

    private _targetHandles: Array<IHandle<PickingInfo>> = [];

    /**
     * 原生传送移动组件
     */
    public teleportation: WebXRMotionControllerTeleportation;

    constructor(xr: WebXRDefaultExperience, scene: Scene, options?: IVRMotionTeleportationOptions) {
        const featureManager = xr.baseExperience.featuresManager;

        const ops = options || {};
        ops.xrInput = ops.xrInput || xr.input;
        ops.floorMeshes = ops.floorMeshes || this.generateDefaultFloorMesh(scene, ops.floorSize, ops.floorTessellation, ops.floorPosition);
        ops.backwardsTeleportationDistance = ops.backwardsTeleportationDistance || 1;
        ops.rotationAngle = ops.rotationAngle || (Math.PI * 0.25);

        this.teleportation = featureManager.enableFeature(WebXRFeatureName.TELEPORTATION, 'stable', ops) as WebXRMotionControllerTeleportation;

        this.teleportation.onTargetMeshPositionUpdatedObservable.add((data: PickingInfo, state: EventState) => {
            for (const handle of this._targetHandles) {
                handle.callback(data);
            }
        });
    }

    /**
     * 添加目标网格位置变化时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onTarget(func: ICallback<PickingInfo>): IHandle<PickingInfo> {
        return CreateHandleTo<PickingInfo>(func, this._targetHandles);
    }

    /**
     * 移除目标网格位置变化时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeTargetHandle(handle: number | IHandle<PickingInfo>): void {
        RemoveHandle(handle, this._targetHandles);
    }

    /**
     * 添加地面网格
     * @param meshes 网格集合
     */
    public addFloorMesh(...meshes: AbstractMesh[]): void {
        for (const mesh of meshes) {
            this.teleportation.addFloorMesh(mesh);
        }
    }

    /**
     * 移除地面网格
     * @param meshes 网格集合
     */
    public removeFloorMesh(...meshes: AbstractMesh[]): void {
        for (const mesh of meshes) {
            this.teleportation.removeFloorMesh(mesh);
        }
    }

    /**
     * 添加障碍网格
     * @param meshes 网格集合
     */
    public addBlockerMesh(...meshes: AbstractMesh[]): void {
        for (const mesh of meshes) {
            this.teleportation.addBlockerMesh(mesh);
        }
    }

    /**
     * 移除障碍网格
     * @param meshes 网格集合
     */
    public removeBlockerMesh(...meshes: AbstractMesh[]): void {
        for (const mesh of meshes) {
            this.teleportation.removeBlockerMesh(mesh);
        }
    }

    /**
     * 添加可吸附点
     * @param points 点集
     */
    public addSnapPoint(...points: Vector3[]): void {
        for (const point of points) {
            this.teleportation.addSnapPoint(point);
        }
    }

    /**
     * 移除可吸附点
     * @param points 点集
     */
    public removeSnapPoint(...points: Vector3[]): void {
        for (const point of points) {
            this.teleportation.removeSnapPoint(point);
        }
    }

    /**
     * 创建默认地面网格
     * @param scene 所在场景
     * @param size 大小
     * @param tessellation 分段数
     * @param position 位置
     * @returns 地面网格
     */
    private generateDefaultFloorMesh(scene: Scene, size?: number, tessellation?: number, position?: Vector3): AbstractMesh[] {
        const floor = MeshBuilder.CreateDisc('vr-default-floor-mesh', {
            radius: size || 50,
            tessellation: tessellation || 12,
        }, scene);
        floor.position = position || new Vector3(0, 0, 0);
        floor.rotation.set(Math.PI * 0.5, 0, 0);
        return [floor];
    }

}


export { VRMotionTeleportation };
export type { IVRMotionTeleportationOptions };